package com.example.java_gobang.api;

import com.example.java_gobang.game.*;
import com.example.java_gobang.mapper.UserInfoMapper;
import com.example.java_gobang.model.UserInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
//处理WebSocket的请求
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    private RoomManager roomManager;

    ObjectMapper object = new ObjectMapper();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //通过WebSocketSession中获取HttpSession中的玩家信息
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
        GameReadyResponse response = new GameReadyResponse();
        //1.判断用户是否登录
        if (userInfo == null) {
            response.setOk(false);
            response.setReason("玩家未进行登录");
            session.sendMessage(new TextMessage(object.writeValueAsString(response)));
            return;
        }

        //2.判断用户是否已经进入游戏房间
        Room room = roomManager.getRoomByUserId(userInfo.getId());
        if (room == null) {
            response.setOk(false);
            response.setReason("玩家尚未匹配到房间");
            session.sendMessage(new TextMessage(object.writeValueAsString(response)));
            return;
        }

        //3.判断用户是否进行了多开
        if (onlineUserManager.getFromGameHall(userInfo.getId()) != null
         || onlineUserManager.getFromGameRoom(userInfo.getId()) != null) {
            //可能一个游戏账号存在于游戏大厅，一个存在于游戏房间中
            response.setOk(true);
            response.setReason("玩家不准进行多开操作！");
            session.sendMessage(new TextMessage(object.writeValueAsString(response)));
            return;
        }

        //4.设置玩家上线到游戏房间
        onlineUserManager.enterGameRoom(userInfo.getId(), session);

        //5.将玩家真正的加入到游戏房间中，进行游戏
        //通过加锁让一个房间的两个客户端不会都成为玩家1，而是让先进入锁的玩家成为玩家1后，再释放锁，后来的玩家成为玩家2
        //不需要对其他房间的客户端进行加锁操作，因为是互不干涉的。
        //如果不加锁，两个客户端并发执行，都拿到了玩家1，此时就没办法跳转到棋盘
        synchronized (room) {
            if (room.getUserInfo1() == null) {
                //当前玩家1并没有加入房间，此时就需要将玩家1加入到游戏房间中
                room.setUserInfo1(userInfo);
                //第一个加入房间的玩家为先手下棋
                room.setWhiteUserId(userInfo.getId());
                System.out.println("玩家1: " + userInfo.getUserName() + "准备就绪！");
                return;
            }

            //玩家2进行加入房间操作
            if (room.getUserInfo2() == null) {
                //当前玩家2并没有加入房间，此时就需要将玩家2加入到游戏房间中
                room.setUserInfo2(userInfo);
                System.out.println("玩家2: " + userInfo.getUserName() + "准备就绪！");

                //两个玩家加入房间后进行通知两个玩家成功进入游戏的房间，给玩家返回一个WebSocket响应
                //通知玩家1
                noticeGameReady(room, room.getUserInfo1(), room.getUserInfo2());
                //通知玩家2
                noticeGameReady(room, room.getUserInfo2(), room.getUserInfo1());
                return;
            }
        }

        //6.如果此时又有一个玩家尝试进入房间，则需要给这个玩家返回一个错误信息
        response.setOk(false);
        response.setReason("当前房间已满，禁止加入！");
        session.sendMessage(new TextMessage(object.writeValueAsString(response)));
    }

    //给玩家返回WebSocket的响应
    private void noticeGameReady(Room room, UserInfo thisUser, UserInfo thatUser) throws IOException {
        GameReadyResponse response = new GameReadyResponse();
        response.setOk(true);
        response.setReason("");
        response.setMessage("gameReady");
        response.setRoomId(room.getRoomId());
        response.setThisUserId(thisUser.getId());
        response.setThatUserId(thatUser.getId());
        response.setWhiteUser(room.getWhiteUserId());
        //把当前数据传回给玩家
        WebSocketSession session = onlineUserManager.getFromGameRoom(thisUser.getId());
        session.sendMessage(new TextMessage(object.writeValueAsString(response)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //落子处理
        //判断当前用户是否已经登录
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
        if (userInfo == null) {
            System.out.println("玩家尚未登录");
            return;
        }

        //玩家登录后则获取游戏房间信息
        Room room = roomManager.getRoomByUserId(userInfo.getId());
        if (room == null) {
            System.out.println("房间不存在");
            return;
        }

        //落子信息处理
        room.putChess(message.getPayload());

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

        if (userInfo == null) {
            return;
        }

        WebSocketSession tmpSession = onlineUserManager.getFromGameRoom(userInfo.getId());
        if (tmpSession == session) {
            onlineUserManager.exitGameRoom(userInfo.getId());
            System.out.println("玩家: " + userInfo.getUserName() + "因为异常退出房间");
        }

        //当前玩家掉线后，判定另一个玩家获胜
        noticeThatUserWin(userInfo);
    }


    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

        if (userInfo == null) {
            return;
        }

        WebSocketSession tmpSession = onlineUserManager.getFromGameRoom(userInfo.getId());
        if (tmpSession == session) {
            onlineUserManager.exitGameRoom(userInfo.getId());
            System.out.println("玩家: " + userInfo.getUserName() + "正常退出房间");
        }

        //当前玩家掉线后，判定另一个玩家获胜
        noticeThatUserWin(userInfo);
    }

    private void noticeThatUserWin(UserInfo userInfo) throws IOException {
        //1.根据当前玩家找到此时两个玩家所处的房间
        Room room = roomManager.getRoomByUserId(userInfo.getId());
        if (room == null) {
            //房间已经被释放，不存在玩家
            System.out.println("当前房间不存在！");
            return;
        }

        //2.判断此时对手玩家是谁
        UserInfo thatUserInfo = userInfo == room.getUserInfo1() ? room.getUserInfo2() : room.getUserInfo1();
        if (thatUserInfo == null) {
            System.out.println("对手玩家不存在!");
            return;
        }

        //3.找到玩家的在线状态
        WebSocketSession session = onlineUserManager.getFromGameRoom(thatUserInfo.getId());
        if (session == null) {
            System.out.println("此时对手玩家也掉线了!");
            return;
        }

        //4.构造通知对手，对手是获胜方
        GameResponse response = new GameResponse();
        response.setWinner(thatUserInfo.getId());
        response.setUserId(thatUserInfo.getId());
        response.setMessage("putChess");
        session.sendMessage(new TextMessage(object.writeValueAsString(response)));

        //5.修改玩家的积分信息
        userInfoMapper.updateWinnerById(thatUserInfo.getId());
        userInfoMapper.updateLoserById(userInfo.getId());

        //6.释放房间对象
        roomManager.remove(room.getRoomId(),room.getUserInfo1().getId(),room.getUserInfo2().getId());
    }
}
